Fix initializr localization bundle path (NPE in MyAppName.init)#4852
Merged
Conversation
The initializr's "include localization bundles" option generated bundles
under common/src/main/resources/messages*.properties, but the CN1 maven
plugin's CSS compiler scans common/src/main/l10n (or i18n) for bundles to
bake into theme.res. The result: Resources.getGlobalResources().getL10N(
"messages", lang) hit a missing resource id at simulator startup and
threw NPE in MyAppName.init -- the project couldn't run.
- GeneratorModel.addLocalizationEntries: write to src/main/l10n so the
bundles actually end up inside theme.res.
- Bootstrap (Java + Kotlin) injected into the starter class is now
null-safe and falls back to the default locale when the device
language has no specific bundle.
- Resources.getL10N / listL10NLocales / l10NLocaleSet now return null
instead of NPE-ing when the bundle id is absent. Defensive change at
the framework level so any project shipping mismatched bundles
degrades gracefully.
- New tests/core/.../ResourcesL10NTest covers the framework null-safety.
- GeneratorModelMatrixTest now asserts bundles land under l10n and are
NOT under src/main/resources (catches the regression at unit-test
time).
- GeneratorModelIntegrationBuildTest now opens common/target/classes/
theme.res after mvn compile and verifies "messages" L10N data is
present for both the default ("") and Hebrew ("he") locales -- the
end-to-end signal the previous tests missed.
While here, harden the simulator's CSS compiler invocation against
stale ~/.codenameone/designer_1.jar:
- New MavenUtils.findDesignerJarInM2 derives the running CN1 version
from the codenameone-core jar's m2 path and resolves the matching
codenameone-designer-<version>-jar-with-dependencies.jar. Any plugin
invocation has already pulled this into m2 as a plugin dependency.
- CSSWatcher and ComponentTreeInspector now prefer codename1.designer.jar
-> m2 designer -> ~/.codenameone fallback (with a clear warning when
the legacy fallback is hit). The build-time CSS goal already used
getDesignerJar() so this only affects the simulator runtime / live
CSS reload paths.
- CompileCSSMojo now invokes the forked CSS compiler with INFO log
level instead of DEBUG so subprocess stack traces are visible without
re-running with -X. This won't fix issue #4850 but makes the next
similar report actionable.
- The four initializr pom templates replace skipexisting="true" with
usetimestamp="true" on the UpdateCodenameOne.jar download so future
installs refresh the updater jar instead of pinning forever to the
first copy.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Collaborator
Author
|
Compared 7 screenshots: 7 matched. |
The previous patch raised the CSS subprocess log level by passing it explicitly at the call site (createJava(LEVEL_INFO)), which bypasses CompileCSSMojoTest's TestCompileCSSMojo.createJava() override -- the test substitutes a RecordingJava there to capture the command line without forking. The override was no longer hit, so the test fell through to a real fork against a stub designer.jar and four tests errored out with "Invalid or corrupt jarfile". Move the INFO log level into a createJava() override on CompileCSSMojo itself. The call site stays at createJava(), so the test override continues to win, and production still gets INFO so subprocess stack traces remain visible without -X. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Contributor
Cloudflare Preview
|
Contributor
✅ Continuous Quality ReportTest & Coverage
Static Analysis
Generated automatically by the PR CI workflow. |
Collaborator
Author
|
Compared 86 screenshots: 86 matched. Native Android coverage
✅ Native Android screenshot tests passed. Native Android coverage
Benchmark ResultsDetailed Performance Metrics
|
Collaborator
Author
The user shared the missing stack trace from issue #4850: at java.base/java.lang.String.substring(String.java:2899) at com.codename1.ui.plaf.UIManager.parseTextFieldInputMode(UIManager.java:2434) at com.codename1.ui.plaf.UIManager.setBundle(UIManager.java:2419) at com.codename1.impl.javase.JavaSEPort.enableAutoLocalizationBundle(...) at com.codename1.impl.javase.JavaSEPort.init(JavaSEPort.java:5598) at com.codename1.impl.CodenameOneImplementation.initImpl(...) at com.codename1.ui.Display.init(Display.java:351) at com.codename1.designer.css.CN1CSSCLI.main(CN1CSSCLI.java:713) This is not a path-related bug -- every initializr-generated project hits it at css-goal time. Three pieces interact: 1. JavaSEPort.findLocalizationDirectory auto-creates src/main/l10n if it is missing, and enableAutoLocalizationBundle installs an AutoLocalizationBundle for it. 2. AutoLocalizationBundle.get echoes any missing key back as its own value -- the simulator's "wormhole" so devs can spot untranslated strings. 3. UIManager.setBundle queries "@im" on every bundle install. With the echo behavior, "@im" -> "@im", which is then tokenized to ["@im"], "@im-@im" is queried (which echoes "@im-@im"), and parseTextFieldInputMode crashes on substring(0, indexOf('=')) because that token has no '=' (range [0, -1) of length 7). The CSS compiler subprocess inherits all of this because CN1CSSCLI.main calls Display.init -> JavaSEPort.init -> enableAutoLocalizationBundle. Fixes: - AutoLocalizationBundle.get returns null for keys starting with '@'. Meta-keys (@rtl, @im, @im-<name>) are configuration entries that callers distinguish from "missing" by checking for null. Echoing the key back is semantically wrong AND broke setBundle. Real meta-key values that exist in the underlying file (e.g. @rtl=true in a Hebrew bundle) are still returned -- only fabrication is suppressed. - UIManager.parseTextFieldInputMode skips tokens without '=' and skips entries whose key isn't a valid integer. Defensive belt-and-suspenders so any bundle with malformed input-mode entries degrades gracefully instead of failing the whole bundle install. - New UIManagerSetBundleTest exercises setBundle against an echo-bundle (matches pre-fix AutoLocalizationBundle behavior) and against directly malformed @im/@im-x entries. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
… fix Per review: silently skipping malformed `@im` tokens hides legitimate bugs in user-supplied bundles. Real malformed input should fail loudly, not be swallowed. The actual root cause -- AutoLocalizationBundle fabricating values for @-prefixed meta-keys -- stays fixed. That's the surgical change: the auto-localize wormhole was returning fake values for keys that callers explicitly use null/non-null to gate features (@im, @rtl, @im-<name>), which is semantically wrong and broke setBundle. Moves the regression coverage from the no-op stub in core to the existing AutoLocalizationBundleTest in the JavaSE port (where the bundle class actually lives), asserting: - @-prefixed meta-keys are NOT auto-fabricated - @-prefixed meta-keys that exist in the underlying file ARE returned Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…wormhole Workaround for the AutoLocalizationBundle @im fabrication crash in shipped Codename One <= 7.0.236. The proper fix lives in JavaSEPort (don't fabricate values for `@`-prefixed meta-keys) and is on this branch, but it requires a new framework release. Until then, every initializr-generated project crashes at css-goal time inside the CSS compiler subprocess (CN1CSSCLI -> Display.init -> JavaSEPort.init -> enableAutoLocalizationBundle -> UIManager.setBundle -> parseTextFieldInputMode on substring(0, -1) for "@im-@im"). Ship `common/src/main/l10n/Bundle.properties` with a single `@im=` entry on every generated project. Two reasons it works: 1. JavaSEPort.findDefaultLocalizationBundleFile prefers Bundle.properties over any other file in src/main/l10n, so the AutoLocalizationBundle loads our stub as its base. 2. With `@im=""` already in the bundle's underlying Hashtable, AutoLocalizationBundle.get("@im") returns "" instead of fabricating "@im". setBundle sees length 0 and skips the input-mode block, so parseTextFieldInputMode is never called. The stub is unconditional (added to every project, with or without localization bundles enabled) because the bug fires regardless -- enableAutoLocalizationBundle auto-creates src/main/l10n in the CSS compiler subprocess even on projects that didn't request localization. The matrix test asserts the stub is present on every generated project combination so this workaround can't silently regress. Once the AutoLocalizationBundle fix lands in a release and the initializr is bumped past it, this stub can be removed. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
4 tasks
shai-almog
added a commit
that referenced
this pull request
May 12, 2026
… jar (#4929) * Fix CSSWatcher live reload: drop stale bindings + extract m2 designer jar Two recent CSS/localization changes regressed the simulator's live CSS reload, in different ways. 1. addThemeProps stomped user edits with stale @cn1-bind entries. PR #4884 added applyThemeBindings() inside UIManager.buildTheme so a single addThemeProps({"@accent-color": ...}) override could retune every var()-bound theme key. But CSSWatcher reloads the theme through the same code path -- and addThemeProps never clears themeConstants. When the user replaced a `var()` rule with a literal in their CSS, the recompiled theme.res no longer emitted the matching `@cn1-bind:<key>` entry, but the previous binding was still sitting in themeConstants. applyThemeBindings happily re-overlaid the user's fresh literal value with the stale binding's resolved value, so the visible change disappeared on every reload. Fix: in buildTheme, before iterating the incoming Hashtable, detect any binding whose subject style key the new load is re-setting without re-asserting the binding alongside, and drop those bindings before the overlay pass runs. Pure `@accent-color` overrides keep working because they don't carry style keys, so no bindings are considered stale. 2. MavenUtils.findDesignerJarInM2 returned the unrunnable wrapper zip. PR #4852 added an m2 fallback for the CSSWatcher's designer-jar lookup, used whenever -Dcodename1.designer.jar isn't passed in (e.g. simulator launched from the IDE rather than `mvn cn1:run`). The helper returned `codenameone-designer-<v>-jar-with-dependencies.jar` directly from m2 -- but that artifact is a zip wrapper containing a single inner designer_1.jar (see maven/designer/pom.xml's antrun step), with no top-level Main-Class manifest. `java -jar wrapper.zip` fails with "no main manifest attribute", the CSS subprocess never starts, and the watcher silently waits for ::refresh:: lines that never come. Fix: mirror AbstractCN1Mojo.getDesignerJar's pattern -- unzip the wrapper to an `<artifact>.jar-extracted/` sibling on demand and return the inner designer_1.jar so `java -jar` actually launches. Tests: - UIManagerThemeBindingsTest gains three regression cases: cssReloadDropsStaleBindingWhenRuleBecomesLiteral (the actual reproducer), cssReloadKeepsBindingWhenStillEmittedTogether (guard against an over-eager fix), and overrideOnlyReloadKeepsBindings (repeated `@accent-color` retunes still work). The first fails before the UIManager fix; all three pass after. - MavenUtilsTest is new and covers the wrapper-vs-inner-jar resolution with five cases: happy path, re-use of extracted inner jar when the wrapper hasn't changed, re-extract when the wrapper mtime advances, null when the core jar isn't in an m2 layout, and null when the designer artifact is missing. To make these actually executable, the javase pom now pins maven-surefire-plugin to 3.2.5 (the parent's 2.21.0 doesn't auto-discover JUnit Jupiter). The pre-existing CSSWatcherTest + LocationSimulationTest + JavaSEPortFontMappingTest in the same module also start running as a side effect. - pr.yml gets a new "Run JavaSE port unit tests" step so this whole test class -- which compiled but never executed -- is wired into CI. Without it, regressions in CSSWatcher/MavenUtils/JavaSEPort helpers would continue to slip through, which was the original gap the user flagged. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Address PR review: harden Zip Slip + install javase deps in CI step - MavenUtils.extractInnerJar no longer derives a File path from ZipEntry.getName(). CodeQL flagged the previous loop as a Zip Slip risk because a wrapper containing `../../etc/passwd` would have been written outside the extraction directory. The wrapper produced by maven/designer/pom.xml has a single designer_1.jar entry by design, so the extractor now (a) writes only to a single fixed destination path under destDir and (b) only matches entries whose literal name equals "designer_1.jar". Anything else is skipped; if the canonical entry is absent, the method throws. Two new MavenUtilsTest cases: refusesPathTraversalEntriesAndDoesNotWriteOutsideExtractDir packs a `../../escaped.txt` entry and asserts no escaped file appears in the temp root; skipsUnexpectedEntriesAndStillExtractsDesignerJar mixes a README and a subdir/other.jar with the real designer_1.jar and asserts only the inner jar lands on disk. - pr.yml's new "Run JavaSE port unit tests" step failed with "Could not find artifact com.codenameone:sqlite-jdbc:jar:8.0-SNAPSHOT" on all three matrix entries (Java 8/17/21). The earlier "Build Codename One" step builds core-unittests with -am, which doesn't install sqlite-jdbc into the local repo. Split the new step into two mvn invocations: first install javase's transitive deps without running their tests, then run javase's tests in isolation. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * UIManager: scope stale-binding drop to addThemeProps, fix iOS hang The previous fix ran the stale-binding preprocessing inside buildTheme, which is also called from the @includeNativeBool layered initial load (setThemePropsImpl -> buildTheme -> Display.installNativeTheme() -> buildTheme(native) -> outer buildTheme(userTheme) continues). After the native theme installs its bindings into themeConstants, the outer call's preprocessing would drop them whenever the user's app theme.css set a literal value for the same UIID -- which the existing iOS / Android screenshot goldens were captured against. The iOS PR check hit this: the device-runner log shows the suite ran fine through ChartCubicLineScreenshotTest and then hung in ChartBarScreenshotTest setup until the 30-minute timeout fired. The inconsistent themeConstants state left over once the layered native bindings were dropped manifests as a hang in chart-component initialization (presumably a Style.derive cycle or similar) rather than as a pixel diff. Move the drop pre-pass out of buildTheme and into a new dropSupersededBindings() called only from addThemeProps. This keeps the CSSWatcher reload fix (the actual reported regression) and the companion regression tests passing, while restoring the original behavior of the layered initial-load path -- bindings declared by the native theme via @includeNativeBool stay live, user-app literals don't silently strip them out. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

Summary
NullPointerExceptioninResources.getL10Nwhen running an initializr project generated with the Include localization bundles option. The bundles were written tocommon/src/main/resources/but the CN1 maven plugin's CSS compiler scanscommon/src/main/l10n/(ori18n/), so they were never baked intotheme.res. Moving them tosrc/main/l10n/makes the data actually present in the resource file at runtime.Resources.getL10N/listL10NLocales/l10NLocaleSetto returnnullinstead of NPE-ing when the bundle id is missing -- defensive across all projects, not just initializr ones.l10n/(and explicitly not underresources/), and the integration build test openstheme.resaftermvn compileand verifies themessagesL10N data is present for default + Hebrew locales.codenameone-corejar's path) over~/.codenameone/designer_1.jar, with a clear warning on the legacy fallback. The build-time CSS goal already pinned viagetDesignerJar(); this only changes the simulator runtime path.CompileCSSMojoforks the CSS compiler with INFO log level instead of DEBUG so subprocess stack traces are visible without-X. Addresses the diagnosability gap behind issue Unable to launch simulator because of Java.lang.StringIndexOutOfBoundsException in latest as of reporting (7.0.236) #4850 (does not fix that bug -- the underlying CSS compiler exception is still in there, but next report will arrive with a usable stack trace).barebones,kotlin,grub,tweet) replaceskipexisting=\"true\"withusetimestamp=\"true\"on theUpdateCodenameOne.jardownload so the bootstrapper refreshes when newer instead of pinning forever.Files changed
CodenameOne/src/com/codename1/ui/util/Resources.java-- null-safegetL10N/listL10NLocales/l10NLocaleSet.Ports/JavaSE/src/.../util/MavenUtils.java-- newfindDesignerJarInM2()helper.Ports/JavaSE/src/.../CSSWatcher.java,ComponentTreeInspector.java-- prefer m2 designer over home-dir fallback.maven/codenameone-maven-plugin/.../CompileCSSMojo.java-- INFO log level for CSS subprocess.scripts/initializr/.../GeneratorModel.java-- bundles tosrc/main/l10n/; bootstrap null/locale fallback.scripts/initializr/.../{barebones,kotlin,grub,tweet}-pom.xml--usetimestampon updater download.scripts/initializr/.../GeneratorModelMatrixTest.java-- assertl10n/location, rejectresources/.scripts/initializr/.../GeneratorModelIntegrationBuildTest.java-- post-compile assertion thattheme.resactually contains themessagesbundle.tests/core/src/com/codename1/ui/util/ResourcesL10NTest.java(new) -- framework regression test for null-safe lookups.Test plan
cd scripts/initializr && ./mvnw -pl common test-- matrix + integration tests pass; integration build verifies theme.res content.cd maven && mvn -Plocal-dev-javase -pl javase -am compile-- JavaSE port compiles.cd maven && mvn -Plocal-dev-javase -pl codenameone-maven-plugin -am compile-- plugin compiles.mvn cn1:run-- simulator starts without the previously-reported NPE; on a non-Hebrew/Arabic device the defaultmessages.propertiesfallback is applied.~/.codenameone/designer_1.jarnow logs the m2 path (or the warning if no m2 designer is available).🤖 Generated with Claude Code